<template>
  <!-- “课程信息”页面，对应的步骤条是"填写课程基本信息" -->
  <div class="app-container" style="padding:15px;">

    <!--
         (二)第二步：提供页面用来收集课程信息的form表单
                    课程标题数据，封装在我们绑定在Vue对象中data属性的course属性中，后端查询到数据后，会封装course中
    -->
    <el-form ref="form" :model="course" label-width="80px">
      <el-form-item label="课程标题">
        <!--
            通过查询数据表edu_course，课程标题是在字段title中存储，
            而后端用来封装查询到的课程列表数据，使用的javabean，Course类中也有相应的title属性
        -->
        <el-input v-model="course.title"/>
      </el-form-item>
      <el-form-item label="课程讲师">
        <!--
            数据表edu_course中有teacher_id字段，Course类中有teacherId
            此处写teacherId，是因为后端查询到数据后，封装在了对应的javabean中
            前端接收的是后端返回来的数据，而不是数据库直接返回来的数据
            要根据后端的JavaBean中的属性名来写
        -->
        <el-select v-model="course.teacherId" placeholder="请选择讲师">
          <!--
              但是我们从course中，拿到的只是teacherId，
              还需要向后端发送请求，前往edu_teacher表中查询讲师
              查询讲师列表后，再进行遍历显示，取出每个讲师对象，赋值给teacherId，在下拉选框中显示
              (a)首先，需要先在Vue对象的methods中，提供方法，向后端发送查询所有讲师列表的请求
              (b)其次，将查询到的数据，封装在Vue对象的data中的属性teachers: []，渲染到当前的div容器中
              (c)接着，对teachers进行遍历
                       ①遍历时，因为遍历的多个数据，每个数据对应一个下拉选框的标签<el-select>，所有要为每个标签指定一个唯一的标识:key="teacher.id"
                       ②:label="teacher.name"表示每个下拉选框的标签要显示的文本，也就是teacher对象里面的name
                       ③:value="teacher.id"表示下拉选框的标签<el-option>的value，要将它赋值给course.teacherId，使用id即可
          -->
          <el-option v-for="teacher in teachers" :key="teacher.id" :label="teacher.name" :value="teacher.id"/>
        </el-select>
      </el-form-item>
      <el-form-item label="课程类别">
        <!--
            课程类别涉及到级联查询：
            之前在subject模块的后端中，已经采取了先从数据库表中，先查询到所有数据，然后再在Java后端代码中将一级分类挑出来，
            添加到一级分类集合中
            接着再挑出二级分类，设置添加到每个一级分类对象的children属性中，返回所有一级分类集合数据给前端
            ---------------------------------------------------------------------------------------------------------
            在这里，也才可以采取相同的做法，就是先从后端中，查询到所有一级分类集合数据，统一返回给前端页面
            然后在前端，对一级分类集合数据遍历，取出每个一级分类对象，赋值给下拉选框的标签<el-select>
            然后再在每个一级分类对象取出其里面的children属性[也就是二级分类对象]，添加到二级分类集合中，再对二级分类集合进行
            遍历，取出每个二级分类对象，赋值给下拉选框的标签<el-select>
            ---------------------------------------------------------------------------------------------------------
            (a)首先，需要先在Vue对象的methods中，提供方法，向后端发送查询课程分类嵌套集合的请求
            (b)其次，将查询到的数据，封装在Vue对象的data中的属性levelOneSubjectList: []，渲染到当前的div容器中
            (c)接着，对levelOneSubjectList进行遍历，取出集合里面每个一级分类对象，赋值给下拉选框的标签<el-select>
            (d)取到一级分类对象以后，给下拉选框设置一个监听内容改变事件函数@change="levelOneSubjectChanged"
               也就是，要监听下拉选框此时此刻，选中的是哪一个一级分类对象
               因为只有根据选中的一级分类对象，才能找出对应的children属性二级分类对象
            (e)在监听内容改变事件函数中，根据选中的一级课程分类的id，查询它的二级分类的集合
               在监听函数中，还要对levelOneSubjectList进行遍历，取出里面每个一级分类对象[因为第c步，是在div标签内遍历的，无法
               在Vue对象中使用]
            (f)得到二级分类集合后，就跟第b步、第c步一样
        -->
        <el-select v-model="course.subjectParentId" placeholder="请选择课程一级分类" @change="levelOneSubjectChanged">
          <el-option v-for="levelOneSubject in levelOneSubjectList" :key="levelOneSubject.id" :label="levelOneSubject.title" :value="levelOneSubject.id"/>
        </el-select>
        <el-select v-model="course.subjectId" placeholder="请选择课程二级分类">
          <el-option v-for="levelTwoSubject in levelTwoSubjectList" :key="levelTwoSubject.id" :label="levelTwoSubject.title" :value="levelTwoSubject.id"/>
        </el-select>
      </el-form-item>
      <el-form-item label="总课时">
        <!--从ElementUI官网InputNumber 计数器中copy，:min="1"表示最小值  -->
        <el-input-number v-model="course.lessonNum" :min="1" controls-position="right"/>
      </el-form-item>
      <el-form-item label="课程简介">
        <!-- 使用第三方工具富文本编辑器Tinymce，收集课程简介信息，收集到course的description属性中 -->
        <tinymce v-model="course.description"/>
      </el-form-item>
      <el-form-item label="课程封面">
        <el-upload
          :show-file-list="false"
          :on-success="handleAvatarSuccess"
          :before-upload="beforeAvatarUpload"
          :action="uploadUrl"
          class="avatar-uploader">
          <img v-if="course.cover" :src="course.cover" class="avatar">
          <i v-else class="el-icon-plus avatar-uploader-icon"/>
        </el-upload>
      </el-form-item>
      <el-form-item label="课程价格">
        <el-input-number v-model="course.price" :min="0" controls-position="right"/>
      </el-form-item>
    </el-form>

    <!-- (一)第一步：提供页面[保存并下一步]按钮 -->
    <div style="text-align:center">
      <!--:disabled="saveBtnDisabled"表示按钮是否禁用，也就是为了避免按钮多次被点击提交  -->
      <el-button :disabled="saveBtnDisabled" type="primary" @click="saveAndNext()">保存并下一步</el-button>
    </div>
  </div>
</template>

<script>
// 导入teacher.js，用来查询所有讲师列表
import teacherApi from '@/api/teacher'
// 导入subject.js，用来查询课程分类嵌套集合
import subjectApi from '@/api/subject'
// 引入富文本编辑器Tinymce，当作当前页面的子组件
import Tinymce from '@/components/Tinymce'
// 导入course.js，用来保存课程信息
import courseApi from '@/api/course'
export default {
  // 注册子组件
  components: { Tinymce },
  data() {
    return {
      saveBtnDisabled: false, // 按钮是否禁用
      course: {
        subjectId: '' // 将course里面的属性subjectId设置默认值为空字符串
      }, // 用来封装后端查询到的课程列表信息，渲染到div容器中
      teachers: [], // 用来封装后端查询到的讲师列表信息，渲染到div容器中，和收集前端填写的课程信息数据
      levelOneSubjectList: [], // 用来封装后端查询到的一级课程分类嵌套集合
      levelTwoSubjectList: [], // 用来封装从一级课程分类中挑出来的二级分类集合
      uploadUrl: 'http://localhost:8120/admin/oss/file/upload?module=cover'
      // 除了提交表单数据，还得携带一个参数，也就是目录\项目名，这个cover是在阿里云OSS的目录名，不是course类中的cover属性
    }
  },
  created() {
    // 页面加载，直接向后端发送查询所有讲师列表的请求
    this.getTeacherList()
    // 页面加载，直接向后端发送查询课程分类嵌套集合的请求
    this.getSubjectsNestedList()
  },
  methods: {
    // [保存并下一步]按钮的单击响应事件
    saveAndNext() {
      // 当点击[保存并下一步]按钮后，对按钮进行禁用
      this.saveBtnDisabled = true
      // this的属性可以参考vueapi: https://cn.vuejs.org/v2/api/index.html
      // this.$parent拿到父组件的vue对象，然后调用vue对象中的data属性里面的active值，为其赋值
      // 当点击[保存并下一步]按钮，跳转到“课程大纲”页面，对应的步骤条是"创建课程大纲"，也就是active = 1
      // this.$parent.active = 1
      // 当用户将课程信息提交给后端，只有成功保存到数据库，才切换到下一个步骤，否则还是停留在此步骤
      // 所以，this.$parent.active = 1就不能写在这里，而是写在saveCourseInfo()中

      // 当用户填写完课程信息后，点击[保存并下一步]按钮后，向后端发送请求的函数，进行判断
      if (!this.$parent.courseId) {
        // 当课程信息不存在时，进行新增
        this.saveCourseInfo()
      } else {
        // 当课程信息存在时，进行更新
        this.updateCourseInfoById()
      }
    },
    // 上传图片之前，回调的钩子函数
    beforeAvatarUpload(file) {
      /**
       * 将图片上传之前，通过参数file获取到图片
       * 然后对图片进行判断
       * JS中的===，表示全等，表示当且仅当两边的数据类型和值都相同的时候才返回true，否则是false
       */
      const isJPG = file.type === 'image/jpeg'// 检查是否是图片'image/jpeg'，是则返回true
      const isLt2M = file.size / 1024 / 1024 < 2// 检查图片的大小
      if (!isJPG) { // 不是图片时，执行里面
        this.$message.error('上传套餐图片只能是 JPG 格式!')
      }
      if (!isLt2M) { // 图片大小大于2M时，执行里面
        this.$message.error('上传套餐图片大小不能超过 2MB!')
      }
      return isJPG && isLt2M// 当两个条件都满足的时候，返回true，才会执行文件上传
    },
    // 图片上传成功后，回调的钩子函数
    handleAvatarSuccess(res, file) {
      // res代表响应对象
      console.log(res)
      // 前端接受上传成功的图片地址，用来做回显的
      this.course.cover = res.data.path
      // 但是发的是异步请求，结果未回来之前，Vue已经渲染数据到div，也就是不会显示图片，此时需要强制刷新页面，再次渲染头像显示
      // $forceUpdate()是Vue提供好的强制刷新函数
      this.$forceUpdate()
    },
    // 查询所有讲师列表，由于之前已经在api/teacher.js中已经写好了相应的api，只要引用过来就行
    getTeacherList() {
      teacherApi.teacherList()
        .then(response => {
          console.log(response.data.items)
          this.teachers = response.data.items
        })
    },
    // 查询课程分类嵌套集合,由于之前已经在api/subject.js中已经写好了相应的api，只要引用过来就行
    getSubjectsNestedList() {
      subjectApi.subjectNestedList()
        .then(response => {
          this.levelOneSubjectList = response.data.items
        })
      /*
    判断，当用户填写完课程基本信息时，又点击上一步回到课程信息页面，那么刚才填写的就已经保存到数据库
    也就是已经存在了courseId
    此时，要根据课程id，查询课程信息，进行回显
    */
      if (this.$parent.courseId) {
      // 回显
        courseApi.getCourseInfoById(this.$parent.courseId).then(response => {
          this.course = response.data.item
          // 根据一级分类查询二级分类集合进行回显
          this.levelOneSubjectList.forEach(levelOneSubject => {
            if (levelOneSubject.id === this.course.subjectParentId) {
              this.levelTwoSubjectList = levelOneSubject.children
              return
            }
          })
        })
      }
    },
    /*
     课程分类下拉选框的内容改变监听函数，当选中对应的一级分类对象的时候，会传一个值过来、
     为什么可以传id过来，因为在上面div容器内的下拉选框标签<el-select>内
     已经将每个一级分类对象的id[:value="levelOneSubject.id"]，
     赋值给下拉选框标签<el-select>标签中的v-model="course.subjectParentId"
     所以监听<el-select>标签的内容发生改变时，传过来的就是levelOneSubject.id
     */
    levelOneSubjectChanged(checkedLevelOneSubjectId) {
      // 每次选择完一级分类对象后，对下拉选框进行监听，遍历二级分类集合之前，先让二级分类对象清空
      // this.levelTwoSubjectList = []//错误的重置清空
      this.course.subjectId = ''
      // console.log(checkedLevelOneSubjectId)// checkedLevelOneSubjectId就是选中的一级分类对象的id
      /*
      遍历一级分类集合，得到每个一级分类对象
      根据遍历得到的一级分类对象的id，跟下拉框选中的一级分类对象id[checkedLevelOneSubjectId]对比
      如果相当，表示当前遍历得到的一级分类对象，就是选中的一级分类对象，然后取出里面children二级分类对象
      在循环中，赋值当前Vue对象的data中的二级分类集合this.levelTwoSubjectList
      return，当levelTwoSubjectList有值后，结束本次循环
      */
      // this代表vue对象，遍历它的data属性中的levelOneSubjectList一级分类集合
      // levelOneSubject代表正在遍历的一级课程分类对象
      this.levelOneSubjectList.forEach(levelOneSubject => {
        if (levelOneSubject.id === checkedLevelOneSubjectId) {
          this.levelTwoSubjectList = levelOneSubject.children
          return
        }
      })
      /*
      此处还存在一个bug：就是当下拉选框选择一级分类对象，再选择相对应的二级分类对象，可以正常显示
                        但是选择完一遍之后，再从下拉框选择别的一级分类对象，二级分类下拉框中，还是上一个显示的二级分类对象
      解决方案：就是每次选择完一级分类对象时，对下拉选框进行监听，再遍历取出二级分类集合之前
               先让二级分类集合清空
               ------------------------------------------------------------------------------------------------------
               注意了，不是将二级分类集合this.levelTwoSubjectList = []清空
               而是将下拉选框中的二级分类集合course.subjectId清空
               为什么呢？
               因为this.levelTwoSubjectList在Vue对象拿到的二级分类集合，它会渲染到div容器中<el-select>
               然后在<el-select>内部的<el-option>进行遍历，取出this.levelTwoSubjectList集合中的每个二级分类对象
               然后再赋值给<el-select>标签的course.subjectId
               而我们要重置的是下拉选框的二级分类集合
               所以，要清空的下拉选框<el-select>标签中二级分类集合course.subjectId
               ------------------------------------------------------------------------------------------------------
               但是，这样又出现了一个问题，将course.subjectId清空后，又得再Vue对象重新监听，重新渲染，重新遍历，重新赋值
               当将<el-option>遍历得到得二级分类对象id[levelTwoSubject.id][得到的是字符串cahr类型]
               重新赋值给<el-select>标签的course.subjectId[默认是number类型]
               如果数据类型对应不上，就无法进行赋值
               解决方案：
                       在Vue的data属性中，将course里面的属性subjectId设置默认值为空字符串[subjectId: '']
                       这样在往div容器进行渲染的时候，course.subjectId就会变成字符串cahr类型
      */
    },
    // 向后端发送请求，保存课程信息的钩子函数
    saveCourseInfo() {
      // 当前Vue对象中的course[用来收集前端填写的课程信息数据]作为参数，传给后端
      courseApi.saveCourseInfo(this.course)
        .then(response => {
          /* 当请求发送成功，获取后端controller返回来的保存成功的课程id，
             因为Chapter和Publish提交课程信息时需要根据课程id进行数据关联
             所以将课程id保存到三个页面的父组件中，方便其它子组件获取
             后端controller返回来的数据：
             return R.ok().data("courseId" ,courseId);
             所以取值的时候，可以根据此来获取response.data.courseId，也可以在浏览器打印查看后获取
          */
          this.$parent.courseId = response.data.courseId
          console.log(this.$parent.courseId) // 浏览器测试打印是否能获取到课程id
          // 什么才向后端发送请求？也就是当用户填写完课程信息后，点击[保存并下一步]按钮后才发送请求，所以需要将此函数，在saveAndNext()进行调用
          // 当用户将课程信息提交给后端，只有成功保存到数据库，才切换到下一个步骤，否则还是停留在此步骤
          this.$parent.active = 1
          // this.$message.success(response.message) //提示信息不够明确
          this.$message.success('课程信息保存成功')
        })
    },
    // 根据课程id，更新课程基本信息
    updateCourseInfoById() {
      courseApi.updateCourseInfoById(this.$parent.courseId, this.course).then(response => {
        this.$message.success('课程信息更新成功')
        this.$parent.active = 1
      })
    }
  }
}
</script>

<style>
  /* 上传图片<el-upload>的样式 */
  .avatar-uploader .el-upload {
    border: 1px dashed #d9d9d9;
    border-radius: 6px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
  }
  .avatar-uploader .el-upload:hover {
    border-color: #409EFF;
  }
  .avatar-uploader-icon {
    font-size: 28px;
    color: #8c939d;
    width: 356px;
    height: 178px;
    line-height: 178px;
    text-align: center;
  }
  .avatar {
    width:356px;
    height: 178px;
    display: block;
  }
</style>
